﻿using Microsoft.AspNetCore.Authorization;
using PHMEE.Managers.BaseInfo.Vessels;
using PHMEE.ToolKits;
using System.Linq.Dynamic.Core;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using static PHMEE.PHMEEAdminPermissions;

namespace PHMEE.Admin
{
    public class PointAppService : PHMEEAdminAppService, IPointAppService
    {
        private readonly IPointManager pointManager;
        private readonly IPointRepository pointRepository;

        public PointAppService(IPointManager pointManager, IPointRepository pointRepository)
        {
            this.pointManager = pointManager;
            this.pointRepository = pointRepository;
        }

        [Authorize(PHMEEAdminPermissions.Points.Create)]
        public async Task<Guid> CreatePointAsync(PointCreateDto input)
        {
            var point = await pointManager.CreatePoint(input.Type, input.Name, input.NameEn, input.OwnerId);

            point.Tag = input.Tag;

            point.Category = input.Category;

            point.OwnerId = input.OwnerId;

            point.OwnerType = input.OwnerType;

            point.Type = input.Type;

            point.Unit = input.Unit;

            point.SerializeProperties(input.DerivedPropertiesJson);

            await pointRepository.PhmeeCreate(point);

            return point.Id;
        }

        [Authorize(PHMEEAdminPermissions.Points.Update)]
        public async Task UpdatePointAsync(PointUpdateDto input)
        {
            var query = await pointRepository.PhmeeGetQuery();

            var (expression, localizedName) = pointManager.GetPointExpression(input.Name, input.NameEn, input.OwnerId);

            if (query.Where(expression).Any(item => item.Id != input.Id))
            {
                throw new UserFriendlyException(L[PHMEEDomainErrorCodes.PointNameAlreadyExist], PHMEEDomainErrorCodes.PointNameAlreadyExist).WithData("Name", localizedName);
            }

            var point = await pointRepository.PhmeeGetById(input.Id);

            if (point == null)
            {
                throw new UserFriendlyException(L[PHMEEDomainErrorCodes.PointNotFound], PHMEEDomainErrorCodes.PointNotFound);
            }

            point.SetName(input.Name);

            point.SetNameEn(input.NameEn);

            point.Tag = input.Tag;

            point.Unit = input.Unit;

            point.SerializeProperties(input.DerivedPropertiesJson);

            await pointRepository.PhmeeUpdate(point);
        }

        [Authorize(PHMEEAdminPermissions.Points.Delete)]
        public async Task DeletePointAsync(Guid id)
        {
            await pointRepository.PhmeeDelete(id);
        }

        [Authorize(PHMEEAdminPermissions.Points.Default)]
        public async Task<PagedResultDto<PointDto>> GetPagedPointsAsync(PointListDto input)
        {
            var query = await pointRepository.PhmeeGetQuery();

            query = query.WhereIf(input.OwnerId != null, item => item.OwnerId == input.OwnerId);

            query = query.WhereIf(!string.IsNullOrEmpty(input.Name), item => item.Name.Contains(input.Name) || item.NameEn.Contains(input.Name));

            query = query.WhereIf(input.Category != null, item => item.Category == input.Category);

            if (input.Sorting != null)
            {
                query = query.OrderBy(input.Sorting);
            }
            else
            {
                query = query.OrderByDescending(item => item.CreationTime);
            }

            var totalCount = query.Count();

            if (input.IsPaged)
            {
                query = query.PageBy(input.SkipCount, input.MaxResultCount);
            }

            var points = await AsyncExecuter.ToListAsync(query);

            var items = ObjectMapper.Map<List<Point>, List<PointDto>>(points);

            return new PagedResultDto<PointDto>(totalCount, items);
        }

        [Authorize(PHMEEAdminPermissions.Points.Default)]
        public async Task<PointDto> GetPointByIdAsync(Guid id)
        {
            Point point = await pointRepository.PhmeeGetById(id);

            return ObjectMapper.Map<Point, PointDto>(point);
        }

        public List<OperationOptionInt> GetPointCategories()
        {
            var localizedName = $"{PhmeeConst.BaseInfoTypeLocalizedPointCategory}";

            var options = pointManager.GetPointCategories().Select(item => new OperationOptionInt(item.Key, L[$"{localizedName}:{item.Value?.ToString()}"])).ToList();

            return options;
        }

        public List<OperationOptionInt> GetPointTypes(PointCategoryEnum pointCategory)
        {
            var localizedName = $"{PhmeeConst.BaseInfoTypeLocalizedPoint}";

            var options = pointManager.GetPointTypes(pointCategory).Select(item => new OperationOptionInt(item.Key, L[$"{localizedName}:{item.Value?.ToString()}"])).ToList();

            return options;
        }

        public List<OperationOptionInt> GetPointPositions()
        {
            var localizedName = $"{PhmeeConst.BaseInfoTypeLocalizedPointPosition}";

            var options = pointManager.GetPointPositions().Select(item => new OperationOptionInt(item.Key, L[$"{localizedName}:{item.Value?.ToString()}"])).ToList();

            return options;
        }

        public List<OperationOptionInt> GetPointDirections()
        {
            var localizedName = $"{PhmeeConst.BaseInfoTypeLocalizedPointDirection}";

            var options = pointManager.GetPointDirections().Select(item => new OperationOptionInt(item.Key, L[$"{localizedName}:{item.Value?.ToString()}"])).ToList();

            return options;
        }

        public List<OperationOptionInt> GetPointDiagnosisAlgs()
        {
            var localizedName = $"{PhmeeConst.BaseInfoTypeLocalizedPointDiagnosisAlg}";

            var options = pointManager.GetPointDiagnosisAlgs().Select(item => new OperationOptionInt(item.Key, L[$"{localizedName}:{item.Value?.ToString()}"])).ToList();

            return options;
        }

        public async Task<List<OperationOptionInt>> GetPointTagOptions(Guid ownerId, string ownerType)
        {
            var dict = await pointManager.GetPointTagOptions(ownerId, ownerType);

            var localizedName = $"{PhmeeConst.BaseInfoTagLocalized}";

            var options = dict.Select(item => new OperationOptionInt(item.Key, L[$"{localizedName}:{item.Value?.ToString()}"])).ToList();

            return options;
        }

    }
}
